using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using AIFSContentManagementSystem;
using AIFSContentManagementSystem.Criteria;
using AIFSDataAccessLayer;
using AIFSDataAccessLayer.Utilities;
using AIFSDataAccessLayer.Exceptions;

namespace AIFSCMS.Infrastructure.Repositories
{
    using EntityMappers;
    using ProxyModels;

    public class CriteriaRepository : ICriteriaRepository
    {
        IRepositoryMap repositoryMap;
        IDataExecutionContext dataExecutionContext;

        public IRepositoryMap RepositoryMap
        {
            get
            {
                return repositoryMap;
            }
        }

        private IDataExecutionContext DataExecutionContext
        {
            get { return dataExecutionContext; }
        }

        public CriteriaRepository(IRepositoryMap RepositoryMap, IDataExecutionContext DataExecutionContext)
        {
            this.repositoryMap = RepositoryMap;
            this.dataExecutionContext = DataExecutionContext;
        }

        public Criterion GetCriterion(int? CriterionId)
        {
            try
            {
                if (TypeUtil.ValueOrNull(CriterionId) == null)
                {
                    throw new AIFSCMSInfrastructureException("Criterion Id is required to retrieve a Criterion");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetCriteria");

                cmd.CreateParameter("CRITERIA_ID", CriterionId);

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);

                //Return a proxy to support lazy loading
                CriteriaMapper mapper = new CriteriaMapper();
                CriterionProxy proxy = mapper.MapSingleRow(dt);

                if (proxy == null)
                    return null;

                proxy.RepositoryMap = this.RepositoryMap;
                if (this.RepositoryMap.DisableLazyLoad)
                {
                    proxy.EagerLoad();
                }

                return proxy;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve criterion", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving criterion", ex);
            }
        }

        public List<ExpressionElement> GetExpressionElement(Criterion Criterion)
        {
            try
            {
                if (Criterion == null || TypeUtil.ValueOrNull(Criterion.CriteriaId) == null)
                {
                    throw new AIFSCMSInfrastructureException("Criterion is required to retrieve expressions");
                }

                List<ExpressionElement> elements = new List<ExpressionElement>();

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetExpression");

                cmd.CreateNullParameter("EXPRESSION_ID");
                cmd.CreateParameter("CRITERIA_ID", Criterion.CriteriaId);

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);

                ExpressionElementMapper mapper = new ExpressionElementMapper();
                elements = mapper.Map(dt);

                return elements;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve expressions for the criterion", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving expression for the criterion", ex);
            }
        }

        public ExpressionElement GetExpressionElement(int? CriterionExpressionId)
        {
            try
            {
                if (TypeUtil.ValueOrNull(CriterionExpressionId) == null)
                {
                    throw new AIFSCMSInfrastructureException("Criterion Expression Id is required to retrieve the element");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetExpression");

                cmd.CreateParameter("EXPRESSION_ID", CriterionExpressionId);
                cmd.CreateNullParameter("CRITERIA_ID");

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);

                ExpressionElementMapper mapper = new ExpressionElementMapper();
                ExpressionElement element = mapper.MapSingleRow(dt);

                return element;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve expression", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving expression", ex);
            }
        }

        public void Save(Criterion Criterion)
        {
            try
            {
                if (Criterion == null)
                {
                    throw new AIFSCMSInfrastructureException("Criterion is empty. Nothing to save.");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("SaveCriterion");

                cmd.CreateParameter("CRITERIA_ID", Criterion.CriteriaId);
                cmd.CreateParameter("USER_ID", Criterion.LastUserId);
                cmd.CreateParameter("LAST_TIMESTAMP", Criterion.LastTimestamp);

                Criterion.CriteriaId = int.Parse(this.DataExecutionContext.ExecuteScalar(cmd).ToString());

                //Delete all existing expressions for this criterion
                DeleteExpression(Criterion);

                //Save the new expressions
                if (Criterion.CriteriaExpression == null)
                    Criterion.CriteriaExpression = new List<Expression>();

                foreach (Expression expression in Criterion.CriteriaExpression)
                {
                    if (string.IsNullOrEmpty(expression.ExpressionElement.LastUserId))
                        expression.ExpressionElement.LastUserId = Criterion.LastUserId;

                    expression.ExpressionElement.CriteriaId = Criterion.CriteriaId;
                    Save(expression);
                }
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve expression", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving expression", ex);
            }
        }

        private void Save(Expression Expression)
        {
            try
            {
                if (Expression == null)
                {
                    throw new AIFSCMSInfrastructureException("Expression is empty. Nothing to save.");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("SaveExpression");

                cmd.CreateParameter("CRITERIA_ID", Expression.ExpressionElement.CriteriaId);
                cmd.CreateParameter("LEFT_OPERAND", Expression.ExpressionElement.LeftOperand);
                cmd.CreateParameter("TYPE", Expression.DataType);
                cmd.CreateParameter("OPER", Expression.ExpressionElement.Oper);
                cmd.CreateParameter("RIGHT_OPERAND", Expression.ExpressionElement.RightOperand);
                cmd.CreateParameter("USER_ID", Expression.ExpressionElement.LastUserId);

                Expression.ExpressionElement.ExpressionId = int.Parse(this.DataExecutionContext.ExecuteScalar(cmd).ToString());
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve expression", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving expression", ex);
            }
        }

        private void DeleteExpression(Criterion Criterion)
        {
            try
            {
                if (Criterion == null || TypeUtil.ValueOrNull(Criterion.CriteriaId) == null)
                {
                    throw new AIFSCMSInfrastructureException("Criterion Id is required to delete expressions");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("DeleteExpressionForCriterion");

                cmd.CreateParameter("CRITERIA_ID", Criterion.CriteriaId);

                int rowsaffected = this.DataExecutionContext.ExecuteNonQuery(cmd);
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not delete expressions", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when deleting expressions", ex);
            }
        }
    }
}
